package ypmits.sound.totest {
	import flash.events.SampleDataEvent;
	import flash.media.Sound;
	import flash.utils.ByteArray;

	/**
	 * @author Andre Michelle - http://blog.andre-michelle.com
	 */
	public class PitchShift extends ASoundEffect {

		private const BLOCK_SIZE : int = 3072;
		public var _target : ByteArray = new ByteArray( );
		private var _position : Number = 0;
		private var _rate : Number = 1;

		
		public function PitchShift( mp3 : Sound ) {
			super( mp3 );
		}

		
		public function get rate() : Number {
			return _rate;
		}

		
		public function set rate( value : Number ) : void {
			value < 0 ? value = 0 : null;
			_rate = value;
		}

		
		override public function sampleDataHandler( event : SampleDataEvent ) : void {
			//-- REUSE INSTEAD OF RECREATION
			_target.position = 0;
			
			//-- SHORTCUT
			var data : ByteArray = event.data;
			
			var scaledBlockSize : Number = BLOCK_SIZE * _rate;
			var positionInt : int = _position;
			var alpha : Number = _position - positionInt;

			var positionTargetNum : Number = alpha;
			var positionTargetInt : int = -1;

			//-- COMPUTE NUMBER OF SAMPLES NEED TO PROCESS BLOCK (+2 FOR INTERPOLATION)
			var need : int = Math.ceil( scaledBlockSize ) + 2;
			
			//-- EXTRACT SAMPLES
			var read : int = _mp3.extract( _target, need, positionInt );

			var n : int = read == need ? BLOCK_SIZE : read / _rate;

			var l0 : Number;
			var r0 : Number;
			var l1 : Number;
			var r1 : Number;

			for( var i : int = 0 ; i < n ; ++i ) {
				//-- AVOID READING EQUAL SAMPLES, IF RATE < 1.0
				if( int( positionTargetNum ) != positionTargetInt ) {
					positionTargetInt = positionTargetNum;
					
					//-- SET TARGET READ POSITION
					_target.position = positionTargetInt << 3;
	
					//-- READ TWO STEREO SAMPLES FOR LINEAR INTERPOLATION
					l0 = _target.readFloat( );
					r0 = _target.readFloat( );

					l1 = _target.readFloat( );
					r1 = _target.readFloat( );
				}
				
				//-- WRITE INTERPOLATED AMPLITUDES INTO STREAM
				data.writeFloat( l0 + alpha * ( l1 - l0 ) );
				data.writeFloat( r0 + alpha * ( r1 - r0 ) );
				
				//-- INCREASE TARGET POSITION
				positionTargetNum += _rate;
				
				//-- INCREASE FRACTION AND CLAMP BETWEEN 0 AND 1
				alpha += _rate;
				while( alpha >= 1.0 ) --alpha;
			}
			
			//-- FILL REST OF STREAM WITH ZEROs
			if( i < BLOCK_SIZE ) {
				while( i < BLOCK_SIZE ) {
					data.writeFloat( 0.0 );
					data.writeFloat( 0.0 );
					
					++i;
				}
			}

			//-- INCREASE SOUND POSITION
			_position += scaledBlockSize;
		}
	}
}